//
//  LLBGCDTaskQueue.m
//  TestApp
//
//  Created by lianglibaoM2 on 2024/8/2.
//

#import "GCDTaskQueue.h"
#import <os/lock.h>

@interface GCDTask()
@property (nonatomic, strong) NSMutableArray<GCDTask *> *dependencies;
@property (nonatomic, copy) NSString *name;

@end

@implementation GCDTask

- (instancetype)initWithBlock:(void (^)(void))block priority:(GCDTaskPriority)priority {
    self = [super init];
    if (self) {
        _taskBlock = [block copy];
        _priority = priority;
        _dependencies = [NSMutableArray array];
        _cancelled = NO;
    }
    return self;
}

- (void)addDependency:(GCDTask *)task {
    [_dependencies addObject:task];
}

- (void)cancel {
    _cancelled = YES;
}

@end

@implementation GCDTaskQueue {
    dispatch_queue_t _queue;
    dispatch_semaphore_t _semaphore;
    NSMutableArray<GCDTask *> *_tasks;
    NSMutableSet<GCDTask *> *_runningTasks;
    os_unfair_lock _lock;
}

- (instancetype)init {
    self = [super init];
    if (self) {
        _queue = dispatch_queue_create("com.example.TaskQueue", DISPATCH_QUEUE_SERIAL);
        _tasks = [NSMutableArray array];
        _runningTasks = [NSMutableSet set];
        _maxConcurrentTasks = 1;
        _semaphore = dispatch_semaphore_create(_maxConcurrentTasks);
        _lock = OS_UNFAIR_LOCK_INIT;
    }
    return self;
}

- (void)setMaxConcurrentTasks:(NSInteger)maxConcurrentTasks {
    _maxConcurrentTasks = maxConcurrentTasks;
    _semaphore = dispatch_semaphore_create(maxConcurrentTasks);
}

- (void)addTask:(GCDTask *)task {
    os_unfair_lock_lock(&_lock);
    [_tasks addObject:task];
    task.name = [NSString stringWithFormat:@"task%lu", _tasks.count];
    [_tasks sortUsingComparator:^NSComparisonResult(GCDTask *t1, GCDTask *t2) {
        return t1.priority < t2.priority;
    }];
    os_unfair_lock_unlock(&_lock);
}

- (void)start {
    dispatch_async(_queue, ^{
        while (YES) {
            GCDTask *task = [self dequeueTask];
            if (task) {
                [self runTask:task];
            } else {
                break;
            }
        }
    });
}

- (GCDTask *)dequeueTask {
    os_unfair_lock_lock(&_lock);
    GCDTask *task = nil;
    for (GCDTask *t in _tasks) {
        if ([self dependenciesResolvedForTask:t]) {
            task = t;
            break;
        }
    }
    if (task) {
        [_tasks removeObject:task];
    }
    os_unfair_lock_unlock(&_lock);
    return task;
}

- (void)runTask:(GCDTask *)task {
    dispatch_block_t block = dispatch_block_create(0, ^{
        if (!task.isCancelled) {
            task.taskBlock();
        }
        [self completeTask:task];
    });

    os_unfair_lock_lock(&_lock);
    [_runningTasks addObject:task];
    os_unfair_lock_unlock(&_lock);

    dispatch_semaphore_wait(_semaphore, DISPATCH_TIME_FOREVER);
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), block);
}

- (BOOL)dependenciesResolvedForTask:(GCDTask *)task {
    for (GCDTask *dependency in task.dependencies) {
        if (![_runningTasks containsObject:dependency] &&
            [_tasks containsObject:dependency]) {
            return NO;
        }
    }
    return YES;
}

- (void)completeTask:(GCDTask *)task {
    os_unfair_lock_lock(&_lock);
    [_runningTasks removeObject:task];
    os_unfair_lock_unlock(&_lock);
    dispatch_semaphore_signal(_semaphore);
}

- (void)cancelAllTasks {
    os_unfair_lock_lock(&_lock);
    for (GCDTask *task in _tasks) {
        [task cancel];
    }
    [_tasks removeAllObjects];
    os_unfair_lock_unlock(&_lock);
}

@end

